/*
 * Session.cpp
 *
 *  Created on: Feb 1, 2015
 *      Author: wasp
 */

#include "Session.hpp"
#include <huaes.h>
#include <sbreturn.h>
#include <hurandom.h>
#include <QDebug>
#include "../base64.h"

namespace de
{
    namespace heimweghelden
    {
        namespace security
        {
            /**
             * Constructor for protection mode
             * The user creates a session key
             */
            Session::Session(sb_GlobalCtx ctx, sb_RNGCtx rngCtx)
            {
                master = true;
                sbCtx = ctx;
                QByteArray keyBytes(16, 0);
                _status = hu_RngGetBytes(rngCtx, keyBytes.length(),(unsigned char *) keyBytes.data(), sbCtx);
                if (_status != SB_SUCCESS){
                    qDebug() << "hu_RngGetBytes failed";
                    return;
                }

                QString keybase64(keyBytes.toBase64().data());
                keyBase64String = keybase64;


                _status = hu_AESParamsCreate(SB_AES_ECB, SB_AES_128_BLOCK_BITS, NULL, NULL, &_aesParams, sbCtx);
                if (_status != SB_SUCCESS){
                    qDebug() << "hu_AESParamsCreate failed";
                    return;
                }

                hu_AESEncryptKeySet(_aesParams, keyBytes.length() * 8, (unsigned char *) keyBytes.constData(), &_aesKey, ctx);
                //hu_AESKeySet(_aesParams, keyBytes.length() * 8, (unsigned char *) keyBytes.constData(), &_aesKey, ctx);
                if (_status != SB_SUCCESS){
                    qDebug() << "hu_AESEncryptKeySet failed";
                    return;
                }
            }

            /**
             * Constructor for helden mode
             * The user will receive a key
             */
            Session::Session(sb_GlobalCtx ctx, QString base64Key) {
                master = false;
                sbCtx = ctx;
                QByteArray keyBytes = QByteArray::fromBase64(base64Key.toUtf8());
                _status = hu_AESParamsCreate(SB_AES_ECB, SB_AES_128_BLOCK_BITS, NULL, NULL, &_aesParams, sbCtx);
                if (_status != SB_SUCCESS){
                    qDebug() << "hu_AESParamsCreate failed";
                    return;
                }

                hu_AESDecryptKeySet(_aesParams, keyBytes.length() * 8, (unsigned char *) keyBytes.constData(), &_aesKey, ctx);
                if (_status != SB_SUCCESS){
                    qDebug() << "hu_AESDecryptKeySet failed";
                    return;
                }
                keyBase64String = base64Key;
            }

            Session::~Session(){
                if (_aesParams) {
                    if (_aesKey) {
                        hu_AESKeyDestroy(_aesParams, &_aesKey, sbCtx);
                    }
                    hu_AESParamsDestroy(&_aesParams, sbCtx);
                }
            }


            QString Session::encrypt(QString input) {
                if (!master) {
                    qDebug() << "encryption not available";
                    return NULL;
                }
                QByteArray in = input.toUtf8();
                int padLen = 16 - (in.length() % 16);
                for (int i=0; i<padLen; i++) {
                    in.append((char)padLen);
                }
                QByteArray out(in.length(), 0);
                _status = hu_AESEncryptMsg
                (
                        _aesParams,
                        _aesKey,
                        SB_AES_128_BLOCK_BYTES,
                        NULL,
                        in.length(),
                        (const unsigned char *) in.constData(),
                        (unsigned char *) out.data(),
                        sbCtx
                );
                QByteArray b64 = out.toBase64();
                QString str(b64.data());
                return str;
            }

            QString Session::decrypt(QString base64input) {
                if (master) {
                    qDebug() << "decryption not available";
                    return NULL;
                }
                QByteArray input = QByteArray::fromBase64(base64input.toUtf8());
                QByteArray output(input.length(), 0);
                _status = hu_AESDecryptMsg
                (
                        _aesParams,
                        _aesKey,
                        SB_AES_128_BLOCK_BYTES,
                        NULL,
                        input.size(),
                        (const unsigned char *) input.constData(),
                        (unsigned char *) output.data(),
                        sbCtx
                );

                if ( ! removePadding(output)) {
                    return NULL;
                }

                return QString(output.data());
            }

            bool Session::removePadding(QByteArray & out) {
                char paddingLength = out[out.length() - 1];
                if (paddingLength < 1 || paddingLength > 16) {
                    qDebug() << "Invalid padding length. Were the keys good?";
                    out.clear();
                    return false;
                }
                if (paddingLength > out.length()) {
                    qDebug() << "Padding is claiming to be longer than the buffer!";
                    out.clear();
                    return false;
                }
                for (int i = 1; i < paddingLength; ++i) {
                    char next = out[out.length() - 1 - i];
                    if (next != paddingLength) {
                        qDebug() << "Not all padding bytes are correct!";
                        out.clear();
                        return false;
                    }
                }
                out.remove(out.length() - paddingLength, paddingLength);
                return true;
            }
        } /* namespace security */
    } /* namespace heimweghelden */
} /* namespace de */
